﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;

namespace XNAProjectLibrary
{
    public class SoundManager : Microsoft.Xna.Framework.GameComponent                               //soundmanager
    {
        public bool RepeatPlayList = true;                                                          //playlist repeat feature
        private AudioEngine engine;                                                                 //need an audio engine
        private WaveBank waveBank;                                                                  //need a wavebank collection of waves
        private SoundBank soundBank;                                                                //need a soundbank collection of cues

        private Dictionary<string, Cue> cues = new Dictionary<string, Cue>();                       //set up dictionary so we can reach cues by with strings
        private Dictionary<string, AudioCategory> categories =
            new Dictionary<string, AudioCategory>();                                                //same for audiocategory

        private string[] playList;                                                                  //create playlist string array
        private int currentSong;                                                                    //no of current song
        private Cue currentlyPlaying;                                                               //current cue playing

        public SoundManager(Game game, string xactProjectName)                                      //constructors
            : this(game, xactProjectName, xactProjectName)
        { }

        public SoundManager(Game game, string xactProjectName,
                            string xactFileName)
            : this(game, xactProjectName, xactFileName,
                game.Content.RootDirectory + @"\Sounds\")
        { }

        public SoundManager(Game game, string xactProjectName,
                            string xactFileName, string contentPath)
            : base(game)                                                                            //calls base class
        {
            xactFileName = xactFileName.Replace(".xap", "");                                        //xactfilename remove extension

            engine = new AudioEngine(contentPath + xactFileName + ".xgs");                          //create new audioengine
            waveBank = new WaveBank(engine, contentPath + "Wave Bank.xwb");                         //create new wavebank
            soundBank = new SoundBank(engine, contentPath + "Sound Bank.xsb");                      //create new soundbank
        }

        public override void Initialize()
        {
            base.Initialize();                                                                      //init
        }

        public override void Update(GameTime gameTime)
        {
            engine.Update();                                                                        //update engine

            if (currentlyPlaying != null)                                                           //are we in playlist mode?
            {
                if (!currentlyPlaying.IsPlaying)                                                    //current cue playing?
                {                                                                                   //if not go to the next
                    currentSong++;

                    if (currentSong == playList.Length)                                             //if this is the last cue
                    {
                        if (RepeatPlayList)                                                         //if repeating then start from 0
                            currentSong = 0;
                        else
                            StopPlayList();                                                         //else stop this sht
                    }

                    if (currentlyPlaying != null)                                                   //may have been set to null, if we finished our list
                    {
                        currentlyPlaying = soundBank.GetCue(
                            playList[currentSong]);                                                 //get the cue
                        currentlyPlaying.Play();                                                    //play it
                    }
                }
            }

            base.Update(gameTime);                                                                  //base update
        }

        protected override void Dispose(bool disposing)                                             //dispose
        {
            soundBank.Dispose();                                                                    //kill everything
            waveBank.Dispose();
            engine.Dispose();

            playList = null;
            currentlyPlaying = null;
            cues = null;
            soundBank = null;
            waveBank = null;
            engine = null;

            base.Dispose(disposing);                                                                //call baseclass dispose
        }

        public void SetGlobalVariable(string name, float amount)                                    //not sure what this does
        {
            engine.SetGlobalVariable(name, amount);
        }

        private void CheckCategory(string categoryName)                                             //check category 
        {
            if (!categories.ContainsKey(categoryName))
                categories.Add(categoryName, engine.GetCategory(categoryName));
        }

        public void SetVolume(string categoryName, float volumeAmount)                              //set the volume of a category to x
        {
            CheckCategory(categoryName);

            categories[categoryName].SetVolume(volumeAmount);
        }

        public void PauseCategory(string categoryName)                                              //pausecategory
        {
            CheckCategory(categoryName);

            categories[categoryName].Pause();
        }

        public void ResumeCategory(string categoryName)                                             //resumecategory
        {
            CheckCategory(categoryName);

            categories[categoryName].Resume();
        }

        public bool IsPlaying(string cueName)                                                       //isplaying?
        {
            if (cues.ContainsKey(cueName))
                return (cues[cueName].IsPlaying);

            return (false);
        }

        public void Play(string cueName)                                                            //play a cue, string name is passed
        {
            Cue prevCue = null;                                                                     //prevcue = null

            if (!cues.ContainsKey(cueName))                                                         //if new cue doesn't exist
                cues.Add(cueName, soundBank.GetCue(cueName));                                       //add that to cues
            else
            {
                if (cues[cueName].IsPlaying)
                    prevCue = cues[cueName];                                                        //if it is playing 

                cues[cueName] = soundBank.GetCue(cueName);                                          //set up new cue
            }

            if (prevCue == null)
                prevCue = cues[cueName];                                                            //if we weren’t playing, set previous to our current cue name

            try
            {
                cues[cueName].Play();                                                               //try to play it
            }
            catch (InstancePlayLimitException)
            {
                                                                                                    //hit limit exception, set our cue to the previous
                                                                                                    //and let’s stop it and then start it up again ...
                cues[cueName] = prevCue;

                if (cues[cueName].IsPlaying)
                    cues[cueName].Stop(AudioStopOptions.AsAuthored);

                Toggle(cueName);                                                                    //start it up again
            }
        }

        public void Pause(string cueName)                                                           //pause
        {
            if (cues.ContainsKey(cueName))
                cues[cueName].Pause();
        }

        public void Resume(string cueName)                                                          //resume
        {
            if (cues.ContainsKey(cueName))
                cues[cueName].Resume();
        }

        public void Toggle(string cueName)                                                          //toggle
        {
            if (cues.ContainsKey(cueName))
            {
                Cue cue = cues[cueName];

                if (cue.IsPaused)
                {
                    cue.Resume();
                }
                else if (cue.IsPlaying)
                {
                    cue.Pause();
                }
                else                                                                                //played but stopped 
                {
                                                                                                    //need to reget cue if stopped
                    Play(cueName);
                }
            }
            else                                                                                    //never played, need to reget cue
                Play(cueName);
        }

        public void StopAll()                                                                       //stop all cues
        {
            foreach (Cue cue in cues.Values)
                cue.Stop(AudioStopOptions.Immediate);
        }

        public void Stop(string cueName)                                                            //stop cue
        {
            if (cues.ContainsKey(cueName))
                cues[cueName].Stop(AudioStopOptions.Immediate);
            cues.Remove(cueName);
        }

        public void StartPlayList(string[] playList)                                                //starts a new playlist
        {
            StartPlayList(playList, 0);
        }

        public void StartPlayList(string[] playList, int startIndex)                                //start a new playlist at index
        {
            if (playList.Length == 0)
                return;

            this.playList = playList;

            if (startIndex > playList.Length)
                startIndex = 0;

            StartPlayList(startIndex);
        }

        public void StartPlayList(int startIndex)                                                   //start playlist at index
        {
            if (playList.Length == 0)
                return;

            currentSong = startIndex;
            currentlyPlaying = soundBank.GetCue(playList[currentSong]);
            currentlyPlaying.Play();
        }

        public void StopPlayList()                                                                  //stop playlist
        {
            if (currentlyPlaying != null)
            {
                currentlyPlaying.Stop(AudioStopOptions.Immediate);
                currentlyPlaying = null;
            }
        }
    }
}